name: Image Release Build

on:
  push:
    tags:
      - v[0-9]+.[0-9]+.[0-9]+
      - v[0-9]+.[0-9]+.[0-9]+-*

permissions:
  # To be able to access the repository with `actions/checkout`
  contents: read
  # Required to generate OIDC tokens for `sigstore/cosign-installer` authentication
  id-token: write

jobs:
  build-and-push:
    timeout-minutes: 45
    name: Build and Push Images
    environment: release
    runs-on: ubuntu-22.04
    strategy:
      matrix:
        include:
          - name: cilium
            dockerfile: ./images/cilium/Dockerfile

          - name: operator
            dockerfile: ./images/operator/Dockerfile

          - name: operator-aws
            dockerfile: ./images/operator/Dockerfile

          - name: operator-azure
            dockerfile: ./images/operator/Dockerfile

          - name: operator-alibabacloud
            dockerfile: ./images/operator/Dockerfile

          - name: operator-generic
            dockerfile: ./images/operator/Dockerfile

          - name: hubble-relay
            dockerfile: ./images/hubble-relay/Dockerfile

          - name: clustermesh-apiserver
            dockerfile: ./images/clustermesh-apiserver/Dockerfile

          - name: docker-plugin
            dockerfile: ./images/cilium-docker-plugin/Dockerfile

    steps:
      - name: Checkout main branch to access local actions
        uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
        with:
          ref: ${{ github.event.repository.default_branch }}
          persist-credentials: false

      - name: Set Environment Variables
        uses: ./.github/actions/set-env-variables

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0

      - name: Login to DockerHub
        uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
        if: ${{ env.PUSH_TO_DOCKER_HUB == 'true' }}
        with:
          username: ${{ secrets.DOCKER_HUB_RELEASE_USERNAME }}
          password: ${{ secrets.DOCKER_HUB_RELEASE_PASSWORD }}

      - name: Login to quay.io
        uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
        with:
          registry: quay.io
          username: ${{ secrets.QUAY_USERNAME_RELEASE_USERNAME }}
          password: ${{ secrets.QUAY_PASSWORD_RELEASE_PASSWORD }}

      - name: Getting image tag
        id: tag
        run: |
          tag=${GITHUB_REF##*/}
          echo tag=$tag >> $GITHUB_OUTPUT
          if [ "${{ env.PUSH_TO_DOCKER_HUB }}" == "true" ]; then
            echo image_tags=${{ github.repository_owner }}/${{ matrix.name }}:$tag,quay.io/${{ env.QUAY_ORGANIZATION }}/${{ matrix.name }}:$tag >> $GITHUB_OUTPUT
          else
            echo image_tags=quay.io/${{ env.QUAY_ORGANIZATION }}/${{ matrix.name }}:$tag >> $GITHUB_OUTPUT
          fi

      - name: Checkout Source Code
        uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
        with:
          persist-credentials: false

      - name: Release Build ${{ matrix.name }}
        uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5.1.0
        id: docker_build_release
        with:
          provenance: false
          context: .
          file: ${{ matrix.dockerfile }}
          push: true
          platforms: linux/amd64,linux/arm64
          tags: ${{ steps.tag.outputs.image_tags }}
          target: release
          build-args: |
            OPERATOR_VARIANT=${{ matrix.name }}

      - name: Install Cosign
        uses: sigstore/cosign-installer@e1523de7571e31dbe865fd2e80c5c7c23ae71eb4 # v3.4.0

      - name: Sign Container Image
        run: |
          if [ "${{ env.PUSH_TO_DOCKER_HUB }}" == "true" ]; then
            cosign sign -y docker.io/${{ github.repository_owner }}/${{ matrix.name }}@${{ steps.docker_build_release.outputs.digest }}
          fi
          cosign sign -y quay.io/${{ env.QUAY_ORGANIZATION }}/${{ matrix.name }}@${{ steps.docker_build_release.outputs.digest }}

      - name: Install Bom
        shell: bash
        env:
          # renovate: datasource=github-releases depName=kubernetes-sigs/bom
          BOM_VERSION: v0.6.0
        run: |
          curl -L https://github.com/kubernetes-sigs/bom/releases/download/${{ env.BOM_VERSION }}/bom-amd64-linux -o bom
          sudo mv ./bom /usr/local/bin/bom
          sudo chmod +x /usr/local/bin/bom

      - name: Generate SBOM
        shell: bash
        # To-Do: generate SBOM from source after https://github.com/kubernetes-sigs/bom/issues/202 is fixed
        # To-Do: format SBOM output to json after cosign v2.0 is released with https://github.com/sigstore/cosign/pull/2479
        run: |
          bom generate -o sbom_${{ matrix.name }}_${{ steps.tag.outputs.tag }}.spdx \
          --image=quay.io/${{ env.QUAY_ORGANIZATION }}/${{ matrix.name }}:${{ steps.tag.outputs.tag }}

      - name: Attach SBOM to container images
        run: |
          if [ "${{ env.PUSH_TO_DOCKER_HUB }}" == "true" ]; then
            cosign attach sbom --sbom sbom_${{ matrix.name }}_${{ steps.tag.outputs.tag }}.spdx docker.io/${{ github.repository_owner }}/${{ matrix.name }}@${{ steps.docker_build_release.outputs.digest }}
          fi
          cosign attach sbom --sbom sbom_${{ matrix.name }}_${{ steps.tag.outputs.tag }}.spdx quay.io/${{ env.QUAY_ORGANIZATION }}/${{ matrix.name }}@${{ steps.docker_build_release.outputs.digest }}

      - name: Sign SBOM Image
        run: |
          if [ "${{ env.PUSH_TO_DOCKER_HUB }}" == "true" ]; then
            docker_build_release_digest="${{ steps.docker_build_release.outputs.digest }}"
            image_name="docker.io/${{ github.repository_owner }}/${{ matrix.name }}:${docker_build_release_digest/:/-}.sbom"
            docker_build_release_sbom_digest="sha256:$(docker buildx imagetools inspect --raw ${image_name} | sha256sum | head -c 64)"
            cosign sign -y "docker.io/${{ github.repository_owner }}/${{ matrix.name }}@${docker_build_release_sbom_digest}"
          fi

          docker_build_release_digest="${{ steps.docker_build_release.outputs.digest }}"
          image_name="quay.io/${{ env.QUAY_ORGANIZATION }}/${{ matrix.name }}:${docker_build_release_digest/:/-}.sbom"
          docker_build_release_sbom_digest="sha256:$(docker buildx imagetools inspect --raw ${image_name} | sha256sum | head -c 64)"
          cosign sign -y "quay.io/${{ env.QUAY_ORGANIZATION }}/${{ matrix.name }}@${docker_build_release_sbom_digest}"

      - name: Image Release Digest
        shell: bash
        run: |
          mkdir -p image-digest/
          job_name=${{ matrix.name }}
          job_name_capital=${job_name^^}
          job_name_underscored=${job_name_capital//-/_}
          echo "${job_name_underscored}_DIGEST := \"${{ steps.docker_build_release.outputs.digest }}\"" > image-digest/makefile-digest.txt

          echo "### ${{ matrix.name }}" > image-digest/${{ matrix.name }}.txt
          echo "" >> image-digest/${{ matrix.name }}.txt
          if [ "${{ env.PUSH_TO_DOCKERHUB }}" == "true" ]; then
            echo "\`docker.io/${{ github.repository_owner }}/${{ matrix.name }}:${{ steps.tag.outputs.tag }}@${{ steps.docker_build_release.outputs.digest }}\`" >> image-digest/${{ matrix.name }}.txt
          fi
          echo "\`quay.io/${{ env.QUAY_ORGANIZATION }}/${{ matrix.name }}:${{ steps.tag.outputs.tag }}@${{ steps.docker_build_release.outputs.digest }}\`" >> image-digest/${{ matrix.name }}.txt
          echo "" >> image-digest/${{ matrix.name }}.txt

      # Upload artifact digests
      - name: Upload artifact digests
        uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
        with:
          name: image-digest ${{ matrix.name }}
          path: image-digest
          retention-days: 10

  image-digests:
    name: Display Digests
    runs-on: ubuntu-22.04
    needs: build-and-push
    steps:
      - name: Getting image tag
        id: tag
        run: |
          echo tag=${GITHUB_REF##*/} >> $GITHUB_OUTPUT
      - name: Downloading Image Digests
        shell: bash
        run: |
          mkdir -p image-digest/

      - name: Download digests of all images built
        uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe # v4.1.2
        with:
          path: image-digest/

      - name: Image Digests Output
        shell: bash
        run: |
          cd image-digest/
          echo "## Docker Manifests" > ../image-digest-output.txt
          echo "" >> ../image-digest-output.txt
          find -type f -not -name "makefile-digest.txt" | sort | xargs -d '\n' cat >> ../image-digest-output.txt

      - name: Image Makefile Digests
        shell: bash
        run: |
          cd image-digest/
          echo "# File generated by .github/workflows/build-images-releases.yaml; DO NOT EDIT." > ../Makefile.digests
          echo "# Copyright "$(date +'%Y')" Authors of Cilium" >> ../Makefile.digests
          echo "# SPDX-License-Identifier: Apache-2.0" >> ../Makefile.digests
          echo "" >> ../Makefile.digests
          find -type f  -name "makefile-digest.txt" | sort | xargs -d '\n' awk '{print "export " $0}' >> ../Makefile.digests

      # Upload artifact digests
      - name: Upload artifact digests
        uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
        with:
          name: image-digest-output.txt-${{ steps.tag.outputs.tag }}
          path: image-digest-output.txt
          retention-days: 10

      # Upload artifact digests
      - name: Upload artifact digests
        uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
        with:
          name: Makefile.digests-${{ steps.tag.outputs.tag }}
          path: Makefile.digests
          retention-days: 10
